/*
 * biwire.c
 *
 *  Created on: Apr 7, 2012
 *      Author: eug.krashtan@gmail.com
 */

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdint.h>
#include "biwire.h"

// Led blink matrix. Only upper 6 bits used for indication loop
static uint8_t bwsignalsR[] = {
		0x00, 0x00, 0xC0, 0xC0,0x00, 0xA0, 0xA0, 0xFC
};
static uint8_t bwsignalsG[] = {
		0x00, 0xC0, 0x00, 0xC0,0xA0, 0x00, 0x50, 0xC0
};

// LED sequence to indicate
volatile enum SignalNames BwIndicate;
// FSM and sequence counter
static volatile uint8_t BwState;
// "LongPress" counter
static volatile uint8_t btn_cnt;
// Data about button events
volatile uint8_t BwButton;
// Orange color created by mixing green and red
static volatile uint8_t color_mix;
// If not null - delay of led indication, then switch LED off
volatile uint8_t BwLedDelay;

void biwire_init(void)
{
	// Timer interrupt
    TCCR0B = _BV(CS00) | _BV(CS01)  | _BV(WGM02); // use CLK/64 prescale value, clear timer/counter on compareA match
    OCR0A = SCAN_FREQ; // preset timer0 OCR byte
    TIMSK0  = _BV(OCIE0A); // enable Output Compare 0 overflow interrupt

    // PIN init
    DDRC |= _BV(BWPORT1) | _BV(BWPORT2);
	// ADC init
    ADCSRA = _BV(ADEN) | _BV(ADPS2); // Activate ADC with Prescaler 16 --> 1Mhz/16 = 62.5kHz
    ADMUX = ADCPORT | _BV(REFS0) | _BV(REFS1) | _BV(ADLAR);  // Select input pin using MUX, left adjust and Internal reference
    DIDR0 = _BV(ADCPORT); // disable digital input for ADC

    // Set initial state for bw
    BwState = ADC_SET;
    BwIndicate = OneGreen;
    BwButton = BW_EVOPN;
    color_mix = 0;
    BwLedDelay = 0;
}

extern void control_pwm(void);
SIGNAL (SIG_OUTPUT_COMPARE0A)    // handler for Output Compare 0 overflow interrupt
{
	control_pwm();
	if (BwState&ADC_SET) {
		// Prepare to keyscan
		PORTC &= ~_BV(GNDPORT);
		DDRC &= ~_BV(ADCPORT);

		ADCSRA |= _BV(ADSC); //Start conversion
		BwState = ADC_WAIT;
		color_mix = MAX_COLOR_MIX;
		if (BwLedDelay)
			if (!(--BwLedDelay)) {
				BwIndicate = LedOff;
				BwButton |= BW_EVFIN;
			}
	} else {
		if (BwState&ADC_WAIT) {
			// Check if ADC complete
			if (ADCSRA & _BV(ADSC) ) return; // Wait another tick
			PORTB = ADCH;
			if (ADCH > BW_VBTN2) { // Both buttons open
				if (BwButton & (BW_EVBT1|BW_EVBT2)) {
					BwButton = BW_EVENT + BW_EVOPN;
					btn_cnt = 0;
				}
			} else if (ADCH > BW_VBTN1) { // Button 2 pressed
				PORTB ^= 0xff;
				if (BwButton & (BW_EVBT1|BW_EVOPN)) {
					BwButton = BW_EVENT + BW_EVBT2;
					btn_cnt = 1;
				}
			} else { // Button 1 pressed. Add compare values here if you need more than 2 buttons
				if (BwButton & (BW_EVBT2|BW_EVOPN)) {
					BwButton = BW_EVENT + BW_EVBT1;
					btn_cnt = 1;
				}
			}
			if (btn_cnt) {
				if(++btn_cnt > BW_LONG_PRESS) {
					// New event - long press
					BwButton |= BW_EVENT + BW_EVLNG;
					btn_cnt = 0;
				}
			}
			// Cleanup
			if (!(BwButton&(BW_EVBT1|BW_EVBT2|BW_EVOPN))) BwButton = BW_EVOPN;
		}
		if (color_mix++ > MAX_COLOR_MIX) {
			// Get next state (LED sequence)
			BwState = (BwState >> 7) | (BwState << 1); // rotate left, replace by _asm to compact code
			// Indicate
			DDRC |= _BV(ADCPORT);
			PORTC &= ~(_BV(GNDPORT)|_BV(ADCPORT));
			if(bwsignalsR[BwIndicate]&BwState)
				PORTC |= _BV(BWPORT2);
			if((bwsignalsG[BwIndicate]&BwState) && (!(bwsignalsR[BwIndicate]&BwState)) )
				PORTC |= _BV(BWPORT1);
			color_mix = 0;
		}
		if((bwsignalsR[BwIndicate]&BwState) && (bwsignalsG[BwIndicate]&BwState)){
			PORTC ^= (_BV(BWPORT1) | _BV(BWPORT2));
		}
	}
}
